angular.js 坑

实际接触,认识到angularjs冰山一角;
表单是交互中最难的,超过动画;有太多不确定因素,包括验证,数据缓存,提交,更新样式/状态等。

angularjs:https://angularjs.org
angular:https://angular.io/
实际使用的angularjs版本:1.3.15。没有把文档lu一遍
https://code.angularjs.org/1.3.15/docs/api

一 基本

angularjs:MVC,双向数据绑定,依赖注入,指令 …
功能强大,尤其是和表单/数据 操作;

双向数据绑定表单提交时,需要注意提交成功后才可以更新视图
验证功能,自己做了处理;没有用三方

指令/过滤/element/函数 等都实用
没有实用路由功能
事件机制也没有研究,不像react等那么清楚

控制器和依赖注入

$scope,$http,$timeout等需要注入才可以使用,包括自定义的服务。
三种注入方式,上面最合适

$scope相当于作用域
没有实用嵌套,互相传递
$scope的方法:$scope.$apply(), $scope.$watch()

  1. $watch

参考:
详解AngularJS中的$watch

  1. $http有三种方式的缓存可以利用
    1
    2
    3
    4
    .controller('controller', function($scope,$http,$timeout) {
    // 数据缓存
    $http.get('./db/quiz.php?act=get_quiz_report&author=' + encodeURIComponent($scope.oEditQuiz.FAuthor),{cache:true})
    });

参考:
说说Angular $http service中的缓存
ular $http cache学习笔记

angular方法

copy,equals,forEach,element,…

isNumber,isObject没有用
forEach: 和ES5的参数,顺序不一样;如果不支持ES5,可以用ng
copy: 深度克隆
equals: 对象/数值的数值比较
element: jQ方式的dom操作

1
2
3
4
5
6
// jQuery Lite 
angular.element('.table-headFixed_body .table tr').css({background:'#fff'});

// 可以在全局得到$scope绑定数据,控制台可以调试
var appElement = document.querySelector('[ng-controller=controller]');
goScope = angular.element(appElement).scope();

ng-class

对象时: 可以写表达式
className有特殊字符,比如-需要加引号

结合css的优先级,样式切换的话,写一个ng-class(后定义的-优先级高,可以覆盖)就可以了

1
2
3
<div ng-class="{'quiz-bg-danger':quizQuestionView.correct===0}" class="quiz-bg-success">
{{ quizQuestionView.correct?'<?=$langmsg["1324"]?>':'<?=$langmsg["1325"]?>' }}<!-- Correct/Incorrect -->
</div>

下拉列表

可以给默认选项;设置选中项值的类型。
还可以获取之前选项,进行更改前的验证(如果需要的话)。

1
2
3
<select ng-change="doQuizQuestionType('{{quizQuestionEdit.type}}')" ng-model="quizQue stionEdit.type" ng-options="x.id as x.name for x in quizQuestion_type" class="form-control" name="edit_quizType" id="quizQuestion_type"></select>

<select ng-model="oEditQuiz.FQuizSetting.passingType" ng-options="x.id as x.name for x in quizSetting_types" name="quizSetting_type" id="quizSetting_type"></select>

1
2
3
4
5
$scope.quizQuestion_type=[
{id:1,name:'<?=$langmsg["1345"]?>'},/* Multiple Choice */
{id:0,name:'<?=$langmsg["1344"]?>'},/* Multiple Response */
{id:2,name:'<?=$langmsg["1346"]?>'}/* True/False */
];

参考:
AngularJs select绑定数字类型问题
angularjs – ng-change获取新值和原始值

单/多选框 切换

单/多选框用一个(ng-checked)时,双向数据绑定的时候有点抽风。
有时传回来的数组,切换问题的时候竟然变成了对象,然后就会导致没有绑定,视图中不会选中。

可能是因为:开始的时候用了一个input标签,数据绑定方式不是ng的方式。导致数据变为对象;而且,视图也不会根据数据做出正确的更新。
包括答题的时候,userAnswers是undefined的,需要手动换成一个数组。

最后:editMode,quizItem 中换成了两个标签,利用ng单/多选框的使用方式。

  1. editMode,quizItem还是分离了,取消了ng-check,利用ng原生的数据绑定方法。
    但是需要为radio做未选中时数据为0,checkbox切换选中状态,添加了ng-click自己做控制。
    包括没有点击时,默认为undefined的情况。
  2. dQuizReview 自定义样式,也抛弃了input的方式。可以用同一个元素(div/span等)表示了。样式/选中状态都可以灵活控制
  3. CHECK: editMode切换问题的时候,正确选项可能不会正确绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   <!-- dQuizItem -->
<input ng-click="doQuizQuestionAnswers($event,$index)" ng-true-value="1" ng-false-value="0" ng-model="quizQuestionEdit.answers[$index]" ng-show="quizQuestionEdit.type==0" title="<?=$langmsg['1319']?>" name="questionAnswerEdit{{$index}}" type="checkbox">
<input ng-click="doQuizQuestionAnswers($event,$index)" ng-model="quizQuestionEdit.answers[$index]" ng-value="1" ng-hide="quizQuestionEdit.type==0" title="<?=$langmsg['1319']?>" name="questionAnswerEdit" type="radio">
<!-- <input ng-click="doQuizQuestionAnswers($event,$index)" ng-checked="quizQuestionEdit.answers[$index]==1" type="{{quizQuestionEdit.type?'radio':'checkbox'}}" name="{{quizQuestionEdit.type?'questionAnswerEdit':'questionAnswerEdit'+$index}}" title="<?=$langmsg['1319']?>">check for correct answer -->

<!-- dQuizReview -->
<li ng-repeat="option in quizQuestionsReview[quizQuestionReviewIndex].options track by $index">
<div class="quizQuestionReview_ops_answers">
<b ng-show="quizQuestionsReview[quizQuestionReviewIndex]['answers'][$index]==1" class="answerReview-checked"></b>
<span ng-class="{'answerReview-radio':quizQuestionsReview[quizQuestionReviewIndex].type===0}"></span>
</div>
<div class="quizQuestionReview_ops_userAnswers">
<b ng-show="quizQuestionsReview[quizQuestionReviewIndex]['userAnswers'][$index]==1" class="answerReview-checked"></b>
<span ng-class="{'answerReview-radio':quizQuestionsReview[quizQuestionReviewIndex].type===0}"></span>
</div>
<span class="quizQuestionReview_ops_option">
{{quizAnswer_tips[$index]}} {{ option }}
</span>
</li>

AngularJS中的radio和checkbox

不靠谱的ng-checked

以下内容,可能还是因为利用一个input标签导致切换的问题。

  1. 只有ng-checked
    不靠谱啊:第一次进去是好,切换next/previous 单选框不能绑定,多选框少了第一个
    有文章说,单选 ng-checked 还要判断 == ,然并卵

    对于radio的绑定,主要指定属性==true,也就是<input id='sex' name="sex" type="radio" ng-model='sex' ng-checked="sex==true">男
    而不是单纯的指定属性,如果单纯指定属性不返回属性的值
    http://blog.csdn.net/u011127019/article/details/52556781

  2. 拆分成两个input 只有ng-model
    根本没有绑定,即单/多选框不会选中(View中)
  3. ng-model + ng-checked
    和只有 ng-checked 的情况差不多

进阶

自定义过滤

ng-model中不能写过滤,需要自己写指令,结合指令的model配置项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   angular.module('app', [])
.filter('seconds2str',function(){// cc from <docs.videojs.com/utils_format-time.js.html>
return function(seconds){
secodes=seconds<0?0:seconds;
var s=Math.floor(seconds %60);
var m=Math.floor(seconds / 60 %60);
var h=Math.floor(seconds / 3600);
if(isNaN(seconds)||seconds===Infinity){
h=m=s='-';
}
h=(h>0)?h+':':'';
m=((h>0&&m<10)?'0'+m:m)+':';
s=(s<10)?'0'+s:s;
return h+m+s;
}
})
// number过滤器
// <td>{{(quizStu.FTotalAwarded/quizStu.FTotalPoints)*100|number:0}}</td>

参考:
AngularJS - 如何将毫秒转换成xHours和yMins?
angular input输入框中使用filter格式化日期

自定义指令

可配置项还是很多的。return的方式,restrict/link/…,scope/element/…,注入的服务..

输出会在页面加载定义的时候输出一次;余下的输出只有在return 相应的函数/监听才会有输出
自定义指令功能很丰富

自定义指令,大小写:都是小写没问题;
定义时候有大写,html中使用时必须-隔离大写,html中大写没用

获取焦点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   /** 两种方式   
* $timeout的注入方式
* 监听时,是监听到变化时才会执行;所以要先把变量重新定义为false
*/
.directive('focusoncondition', ['$timeout',function ($timeout) {
/* var checkDirectivePrerequisites=function($timeout){
if(!attrs.focusoncondition&&attrs.focusoncondition!=""){
throw "FocusOnCondition missing attribute to evaluate";
}
} */
return {
restrict:"A",
link:function (scope, element, attrs,ctrls) {
// checkDirectivePrerequisites(attrs);
scope.$watch('isFocusOnQuestionTitle',function(cv,ov,){
// scope.$watch(attrs.focusoncondition,function(cv,ov){
// console.log(cv)
if(cv==true){
// $timeout(function(){element[0].focus()});
element[0].focus()
}
})
}
};
}])
// html
//<input focusoncondition="isFocusOnQuestionTitle" autofocus required >

focus - 以 Angular 方式,设置元素焦点
深究AngularJS——如何获取input的焦点(自定义指令)

图片加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	// console.log('redirecitv')
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('load', function () {
// console.log(element)
//call the function that was passed
        scope.$apply(attrs.imageonload);
       });
element.bind('error',function(){
// console.log('error');
});
    }
}
});
// html
//<img imageonload="gotoAS(img.index)" ng-repeat="img in imgList track by $index" ng-src="{{img.src}}" alt="">

参考:
详解Angularjs 如何自定义Img的ng-load 事件
angular实现图片预加载指令
AngularJS中如果ng-src 图片加载失败怎么办

滚动

参考:
1. angularJS-滚动到底部触发事件

1
2
3
4
5
6
7
8
9
10
11
12
13
// <div class="box" when-scrolled="loadMore()">
//滚动指令
pro.directive('whenScrolled', function() {
return function(scope, elm, attr) {
// 内层DIV的滚动加载
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
};
});
};
});

2. 通过AngularJS指令操作DOM
同一个作者相同文章:Angular.JS通过指令操作DOM的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 一个引入jQuery操作DOM的指令如下:
webApp.directive("detailTopStick", ["$timeout", "$window", function ($timeout, $window) {
return {
restrict: "A",
link: function (scope) {
$timeout(function () {
var navbar = $(".navbar-nav");
var navbarOffsetTop = navbar.offset().top;
var headerInfo = $(".header-info");
var headerInfoMarginBottom = parseInt(headerInfo.css("margin-bottom"));
var navbarHeight = parseInt(navbar.css("height"));

angular.element($window).bind("resize", function () { // 窗口绑定resize事件
navbar.css("width", headerInfo.width());
navbarOffsetTop = navbar.offset().top;
scope.$apply();
});

angular.element($window).bind("scroll", function () {
if ($window.scrollY > navbarOffsetTop) {
navbar.css("width", headerInfo.width());
navbar.addClass("detail-navbar-fix");
headerInfo.css("margin-bottom", headerInfoMarginBottom + navbarHeight);
}
else {
navbar.removeClass("detail-navbar-fix");
headerInfo.css("margin-bottom", headerInfoMarginBottom);
}
scope.$apply();
});

navbar.on("click", function () {
if ($window.scrollY > navbarOffsetTop) {
$window.scrollTo(0, navbarOffsetTop);
}
});
});
}
};

}]);

其它:
3. angular 自定义 scroll事件:
一个div里面包了很多个div,外面的div是可以的滚动的,而根据滚动到不同的区块时,每个内在的div要浮现出一块部分

4. Duang~简单实用的angular滚动列表特效(移动端):
做成滚动时固定,然后可以被下一个元素顶上去的效果

5. angularjs 的操作dom一般如何写在指令或者服务中?通过AngularJS指令操作DOM
同一个作者相同文章:Angular.JS通过指令操作DOM的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 一个引入jQuery操作DOM的指令如下:
webApp.directive("detailTopStick", ["$timeout", "$window", function ($timeout, $window) {
return {
restrict: "A",
link: function (scope) {
$timeout(function () {
var navbar = $(".navbar-nav");
var navbarOffsetTop = navbar.offset().top;
var headerInfo = $(".header-info");
var headerInfoMarginBottom = parseInt(headerInfo.css("margin-bottom"));
var navbarHeight = parseInt(navbar.css("height"));

angular.element($window).bind("resize", function () { // 窗口绑定resize事件
navbar.css("width", headerInfo.width());
navbarOffsetTop = navbar.offset().top;
scope.$apply();
});

angular.element($window).bind("scroll", function () {
if ($window.scrollY > navbarOffsetTop) {
navbar.css("width", headerInfo.width());
navbar.addClass("detail-navbar-fix");
headerInfo.css("margin-bottom", headerInfoMarginBottom + navbarHeight);
}
else {
navbar.removeClass("detail-navbar-fix");
headerInfo.css("margin-bottom", headerInfoMarginBottom);
}
scope.$apply();
});

navbar.on("click", function () {
if ($window.scrollY > navbarOffsetTop) {
$window.scrollTo(0, navbarOffsetTop);
}
});
});
}
};
}]);

ng-repeat + ng-model 数据筛选

$_PS: 没有实通

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<input type="text" placeholder="Search Records" ng-model="SearchText" />
<table>
<thead>
<tr>
<th> Name </th>
<th>Date of Birth </th>
<th>Address </th>
<th>Salary </th>
</tr>
</thead>
<tbody>
<tr ng-repeat="employee in employees|filter :SearchText">
<td>{{employee.name }}</td>
<td>{{employee.dateOfBirth }}</td>
<td>{{employee.Address }}</td>
<td>{{employee.Salary }}</td>
<!-- <td>{{employee.Salary| currency }}</td> -->
</tr>
</tbody>
</table>

参考链接:
Search And MultiSearch In AngularJS
Simple search and filter with AngularJs
http://plnkr.co/edit/XklvXtc1AZpndjLvXrh8?p=preview

排序和serach

做了分页:一次取所有数据,拆分数据,单页显示
表格中是部分数据,利用 |filter:x/|orderby:x 只是对表格中有的数据排序/过滤

自己过滤整个数据,根据新数据,制作新的分页

table分页

利用了第一种方式

参考一:无插件
Angular.js+Bootstrap实现表格分页
$_YX: Angular.js+Bootstrap实现表格分页
参考二:插件ng-pagination
angularjs+bootstrap自定义分页
基于Angularjs实现分页
详解angularjs结合pagination插件实现分页功能

表单

下面两个属于相同问题,button默认type为submit,更正type=button

  1. button自动验证required
    参考:
    form表单下的button按钮会自动提交表单的问题

  2. error: “An invalid form control with name=’’ is not focusable.”。修改button类型后解决了
    参考一:修改button:
    An invalid form control with name=’file[]’ is not focusable.间接点击form表单的\出现的问题
    An invalid form control with name=’’ is not focusable. WITHOUT ANY REQUIRED OR HIDDEN INPUTS

    参考二:form标签加 novalidate属性:
    An invalid form control with name=’’ is not focusable

ng-repeat动态添加/删除dom

直接操作dom不明智。
参考:
angular.js 下如何动态插入删除dom节点

自动滚动到底部

操作dom性能是不是不好;有没有其他方式,比如指令。
操作dom的方法是原生js;非angular.element。

和监听window/element滚动事件操作dom不一样,是插入加载数据后自动滚动到底部,非监听滚动到底部加载数据。所以写了一个自动滚动到底部的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
$scope.scrollWindow=function(query){
dquiz('scroll window ...');
// var _el = document.getElementById(id);
var _el = document.querySelector(query);// $_MORE: 要不要换成angular.element
// dlog(_el.scrollTop);
if(_el.scrollHeight > _el.clientHeight){
_el.previousElementSibling.style.paddingRight='17px';
_el.scrollTop = _el.scrollHeight;
}else{
_el.previousElementSibling.style.paddingRight=0;
}

};

ISSUES

  1. input输入值后,再清空,绑定数据为undefined

注意事项:
radio/checkbox 绑定数据异常:数据绑定方式不规范(妄想一个dom利用两个不同的绑定方式);数据切换时,绑定数据为undefined

其它:
关于 AngularJS 的一些问题

knowledge is no pay,reward is kindness
0%